<?php

 /**
  * @package JooS
  * @subpackage Helper
  */

  final class JooS_Helper_Broker implements ArrayAccess {
    const HELPER_INTERFACE = "JooS_Helper_Interface";

    const HELPER_SUBJECT_INTERFACE = "JooS_Helper_Subject";

    private $_helpers = array();

    private $_subject = null;

    private static $_helperBroker = null;

    private static $_loadedHelpers = array();

    private static $_paths = null;

    /**
     * @param string $prefix
     */
    public static function addPrefix($prefix) {
      $prefix = rtrim($prefix, '_') . "_";
      $path = str_replace('_', DIRECTORY_SEPARATOR, $prefix);

      $newPrefix = array(
        "dir" => $path, 
        "prefix" => $prefix
      );

      if (!in_array($newPrefix, self::$_paths))
        self::$_paths[] = $newPrefix;
    }

    /**
     * @return array
     */
    public static function getPrefixes() {
      return self::$_paths;
    }

    /**
     * 
     */
    public static function clearPrefixes() {
      self::$_paths = array();
      self::addPrefix("JooS");
    }

    /**
     *
     * @param JooS_Helper_Subject $subject
     * @return JooS_Helper_Broker
     */
    public static function newInstance($subject) {
      if ($subject instanceof JooS_Helper_Subject) {
        $newInstance = new self();
        $newInstance->_subject = $subject;

        return $newInstance;
      }
      else
        self::throwException("Subject must implement " . self::HELPER_SUBJECT_INTERFACE . " interface");
    }

    public function __call($helper, $argv) {
      self::_getHelperReflection($helper);
    }

    public function __get($helper) {
      if (!$this->__isset($helper))
        self::throwException("Helper '$helper' was not found");
    
      return $this->_helpers[$helper];
    }

    public function __isset($helper) {
      $has = true;
      try {
        $this->_getHelper($helper);
      }
      catch (JooS_Helper_Exception $e) {
        $has = false;
      }
      return $has;
    }

    public function offsetExists($helper) {
      return $this->__isset($helper);
    }

    public function offsetGet($helper) {
      return $this->__get($helper);
    }

    public function offsetSet($offset, $value) {
    }

    public function offsetUnset($offset) {
    }

    private function __construct() {
      if (is_null(self::$_paths)) {
        require_once "JooS/Config.php";

        self::clearPrefixes();
        $prefixes = JooS_Config::Helper_Broker()->prefixes();

        if (is_array($prefixes))
          foreach ($prefixes as $prefix)
            self::addPrefix($prefix);
      }
    }

    private function _getHelper($helper) {
      if (!isset($this->_helpers[$helper])) {
        $this->_helpers[$helper] = self::_getHelperReflection($helper)->newInstance();
        $this->_helpers[$helper]->setSubject($this->_subject);
      }
      return $this->_helpers[$helper];
    }

    private static function _getHelperReflection($helper) {
      if (!isset(self::$_loadedHelpers[$helper])) {
        require_once "JooS/Loader.php";

        $className = null;
        for ($i=sizeof(self::$_paths) - 1; $i>=0; $i--) {
          $className = self::$_paths[$i]["prefix"] . $helper;
          if (JooS_Loader::loadClass($className))
            break;
          else
            $className = null;
        }
        if (is_null($className))
          self::throwException("Helper '$helper' was not found");

        $r_Helper = new ReflectionClass($className);
        if ($r_Helper->implementsInterface(self::HELPER_INTERFACE)) {
          self::$_loadedHelpers[$helper] = $r_Helper;
          $r_Helper->getMethod("init")->invoke(null);
        }
        else
          self::throwException("Helper '$helper' must implement " . self::HELPER_INTERFACE . " interface");
      }
      return self::$_loadedHelpers[$helper];
    }

    private static function throwException($message, $code = 0) {
      require_once "JooS/Helper/Exception.php";

      throw new JooS_Helper_Exception($message, $code);
    }
  }
